////////////////////////////////////////////////////////////////////////////////
//
// $Id$ 
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// 
// This software consists of voluntary contributions is licensed under the 
// MIT <http://www.opensource.org/licenses/mit-license.php>. 
// 
// 
// Portuguese Translation
// 
// ESTE SOFTWARE É FORNECIDO “COMO ESTÁ”, SEM GARANTIAS DE QUALQUER ESPÉCIE, 
// EXPRESSAS OU TÁCITAS, INCLUINDO SEM LIMITAÇÕES, QUAISQUER GARANTIA DE COMERCIABILIDADE, 
// DE ADEQUAÇÃO A FINALIDADES ESPECÍFICAS OU DE NÃO VIOLAÇÃO A DIREITOS DE TERCEIROS. 
// EM NENHUMA HIPÓTESE OS AUTORES OU TITULARES DOS DIREITOS AUTORAIS SERÃO RESPONSÁVEIS 
// POR QUAISQUER RECLAMAÇÕES E/OU INDENIZAÇÕES POR DANOS OU POR OUTROS MOTIVOS, 
// SEJAM ESSES DERIVADOS DE OBRIGAÇÕES CONTRATUAIS OU DE DECISÕES JUDICIAIS, 
// ILÍCITOS CIVIS OU OUTROS MOTIVOS, OS QUAIS VENHAM A OCORRER EM CONSEQÜÊNCIA 
// OU EM RELAÇÃO AO SOFTWARE , A SEU USO OU OUTRA FORMA DE NEGOCIAÇÃO/UTILIZAÇÃO. 
// 
// Esse software consiste de contribuições voluntárias e está sobre a licença
// MIT <http://www.opensource.org/licenses/mit-license.php>.
// 
////////////////////////////////////////////////////////////////////////////////

package br.mcaslib.collections
{
	import flash.events.Event;
	
	import mockolate.mock;
	import mockolate.nice;
	import mockolate.prepare;
	import mockolate.verify;
	
	import mx.collections.errors.ItemPendingError;
	import mx.events.PropertyChangeEvent;
	import mx.events.PropertyChangeEventKind;
	import mx.rpc.AsyncToken;
	import mx.rpc.IResponder;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.Operation;
	import mx.rpc.remoting.RemoteObject;
	
	import org.flexunit.Assert;
	import org.flexunit.async.Async;

	public class ListRemoteViewTest
	{
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------

		
		[Before(async, timeout=5000)]
		public function prepareMocks():void
		{
			Async.proceedOnEvent(this,
				prepare( RemoteObject, Operation ),
				Event.COMPLETE );
		}
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 * validate method test
		 * <p>Situation:</p>
		 * <p>Validate without setup remote object.</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>Throw an error exception because the system doesn't 
		 * know where get remote data from</p>
		 */
		[Test]
		public function validate_noSetupRemoteObject():void {
			var listRemoteView:ListRemoteView = new ListRemoteView();
			try {
				listRemoteView.refresh();
				Assert.fail( "No set up remote object. How does the system get remote data?" );
			} catch ( e:Error ) {
			}
		}
	
		/**
		 * addItemAt method test
		 * <p>Situation:</p>
		 * <p>Add an item into list with few elements (normal situation).</p>
		 * <p>Few elements means all itens in one page.</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>The item will be added into list and should be available to get it</p>
		 */
		[Test]
		public function testAddItemAt():void {
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			
			var mockRemoteObject:RemoteObject = nice( RemoteObject );
			var mockOperation:Operation = nice( Operation );
			
			// Setup listRemoteView
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mock( mockRemoteObject )
				.method( 'getOperation' )
				.args( 'load' )
				.returns( mockOperation )
				.once();
				
			mock( mockOperation )
				.method( 'send' )
				.anyArgs()
				.returns( asyncToken )
				.once();
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = [1,2,3,4,5,6];
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length;
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );
			}
			
			// Add item 
			listRemoteView.addItemAt( 100, 3 );
			
			Assert.assertEquals( "Is it on list?", Number(100), Number(listRemoteView.getItemAt(3)) );
			
			verify( mockRemoteObject );
			verify( mockOperation );
		}
		
		/**
		 * addItemAt method test
		 * <p>Situation:</p>
		 * <p>Add an item into list out of page hit</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>The item is out of page so the user don't see this item therefore
		 * the item will not be added into list.</p>
		 * <p>The list's length will be decreased because this class guess
		 * that the user added this item on sever before.</p>
		 */
		/*
		[Test]
		public function testAddItemAt_noPageHit():void {
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation )
				.once;
			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.once;
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = [1,2,3,4,5,6,7,8,9,10];
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length + 10; // 20 itens just first 10 will be on simulation send 
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			// Add item 
			listRemoteView.addItemAt( 100, 19 );
			
			assertEquals( "Is item on list", Number(-1), Number(listRemoteView.getItemIndex(19)) );
			assertEquals( "Real length", Number(21), Number(listRemoteView.length) );
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock );
		}
		*/

		/**
		 * addItemAt method test
		 * <p>Situation:</p>
		 * <p>Add an item into empty list</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>The item will be added and should be available to get it.</p>
		 */
		/*
		[Test]
		public function testAddItemAt_emptyList():void {
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation )
				.once;
			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.once;
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = new Array();
			var data:Object = new Object();
			data.data = result;
			data.realLength = 0; // Empty list 
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			// Add item 
			listRemoteView.addItem( 100 );
			
			assertEquals( "Is item on list", Number(100), Number(listRemoteView.getItemAt(0)) );
			assertEquals( "Real length", Number(1), Number(listRemoteView.length) );
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock );
		}
		*/
		
		/**
		 * addItemAt method test
		 * 
		 * <p>Situation:</p>
		 * <p>Add an item in list with no length set up from server.</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>Nothing will be done. This element will get when the server
		 * send data to this list. 
		 * This class is a view of data server and should not be use
		 * to change this data.</p>
		 */
		/*
		[Test]
		public function testAddItemAt_noRealLenght():void {
			var listRemoteView:ListRemoteView = new ListRemoteView();
			listRemoteView.remoteObject = new MockRemoteObject();
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			var item:int = 100;
			
			listRemoteView.addItemAt(item,0);
			
			assertEquals("It haven't received nothing from server yet. The lenght should be 0", 0, listRemoteView.length);
		}
		*/

		/**
		 * getItemAt method test
		 * <p>Situation:</p>
		 * <p>Get an item out of range</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>Throw exceptio</p>
		 */
		/*
		[Test]
		public function testGetItemAt_outOfRange():void {
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation )
				.once;
			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.once;
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = [1,2,3,4,5,6,7,8,9,10];
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length + 10; // 20 itens just first 10 will be on simulation send 
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			try {
				listRemoteView.getItemAt( 100 );
				fail( "throw out of range exception" );
			} catch( e:Error ) {
				assertTrue( true );
			}
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock );
		}
		*/

		/**
		 * getItemAt method test
		 * <p>Situation:</p>
		 * <p>Get an item out of page</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>Send remote operation to get more itens and throw ItemPendingError</p>
		 */
		/*
		[Test]
		public function testGetItemAt_outOfPage():void {
			var listRemoteViewVo:ListRemoteViewVo;
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			var mockOperationOutPage:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation, mockOperationOutPage )
				.exactly(2);
 			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.exactly(1);
 			mockOperationOutPage.mock
				.method( 'send' )
				.andCall( 
					function( args:Array ):void {
						listRemoteViewVo = ListRemoteViewVo(args[0]);
					}
				)
				.withAnyArgs
				.andReturn( asyncToken )
				.exactly(1);
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = [1,2,3,4,5,6,7,8,9,10];
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length + 100; // 110 itens just first 10 will be on simulation send 
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			try {
				listRemoteView.getItemAt( 90 );
				fail( "throw ItemPendingError" );
			} catch( e:ItemPendingError ) {
				assertTrue( true );
			}
			
			assertEquals( "Get 90'th item index", Number(90), listRemoteViewVo.index ); 
			assertEquals( "Page length", Number(10), listRemoteViewVo.maxLength );
			assertEquals( "Real length", false, listRemoteViewVo.calculateRealLength );
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock, mockOperationOutPage.mock );
		}
		*/
		
		/**
		 * removeItemAt method test
		 * <p>Situation:</p>
		 * <p>Remove item from list with few elements (normal situation).</p>
		 * <p>Few elements means all itens in one page.</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>The item will be removed from and should not be available to get it</p>
		 */
		/*
		[Test]
		public function testRemoveItemAt():void {
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation )
				.once;
			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.once;
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = [1,2,3,4,5,6,7,8,9,10];
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length 
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			// Remove item 
			assertEquals( "Remove item", Number(3), listRemoteView.removeItemAt( 2 ) );
			assertEquals( "Real length", Number(9), Number(listRemoteView.length) );
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock );
		}
		*/
		
		/**
		 * removeItemAt method test
		 * <p>Situation:</p>
		 * <p>Remove an item from list out of page hit</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>The item is out of page so the user don't see this item therefore
		 * the item will not be removed from list. There is no item on memory
		 * to remove.</p>
		 * <p>The list's length will be decreased because this class guess
		 * that the user added this item on sever before.</p>
		 */
		/*
		[Test]
		public function testRemoveItemAt_noPageHit():void {
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation )
				.once;
			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.once;
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = [1,2,3,4,5,6,7,8,9,10];
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length 
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			// Remove item 
			assertEquals( "Remove item", Number(3), listRemoteView.removeItemAt( 2 ) );
			assertEquals( "Real length", Number(9), Number(listRemoteView.length) );
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock );
		}
		*/
		
		/**
		 * behaviour test
		 * <p>Situation:</p>
		 * <p>Change a property of an item on list.</p>
		 * 
		 * <p>Expectation:</p>
		 * <p>The event changeProperty should be dispatch</p>
		 */
		/*
		[Test]
		public function testEventChange():void {
			var eventPropertyChange:PropertyChangeEvent;
			var asyncToken:AsyncToken = new AsyncToken(null);
			var listRemoteView:ListRemoteView = new ListRemoteView();
			var mockRemoteObject:MockRemoteObject = new MockRemoteObject();
			var mockOperation:MockOperation = new MockOperation();
			
			listRemoteView.remoteObject = mockRemoteObject;
			listRemoteView.operation = 'load';
			listRemoteView.maxPageLength = 10;
			listRemoteView.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE,
				function (event:PropertyChangeEvent):void {
					eventPropertyChange = event
				}
			);
			
			// Setup mock expectation and method returns
			mockRemoteObject.mock
				.method( 'getOperation' )
				.withArgs( 'load' )
				.andReturn( mockOperation )
				.once;
			mockOperation.mock
				.method( 'send' )
				.withAnyArgs
				.andReturn( asyncToken )
				.once;
			
			// Validate list - get data from remote server	
			listRemoteView.refresh();

			// Setup simulation data from remote server
			// inject this data into list 
			var result:Array = new Array();
			var item:ItemBindable = new ItemBindable();
			item.propWillChange = 100;
			result.push(item);
			var data:Object = new Object();
			data.data = result;
			data.realLength = result.length;
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, false, true, data, asyncToken );
			for each( var iResponder: IResponder in asyncToken.responders ) {
				iResponder.result( resultEvent );				
			}
			
			// Change property item
			item.propWillChange = 500;
			
			assertNotNull("Receive event", eventPropertyChange ); 
			assertEquals( "Same property", "0.propWillChange", eventPropertyChange.property ); // 0. is the item index
			assertEquals( "Old value", 100, eventPropertyChange.oldValue );
			assertEquals( "New value", 500, eventPropertyChange.newValue );
			
			Mock.verify( mockRemoteObject.mock, mockOperation.mock );
		}
		*/
	}
}

/*
import mx.rpc.AbstractOperation;
import mx.rpc.AsyncToken;
import mx.rpc.remoting.Operation;
import mx.rpc.remoting.RemoteObject;
import mx.rpc.AbstractService;
import com.anywebcam.mock.Mock;
*/

/**
 * Mock remote object
 * 
 * @private
 */
/*
class MockRemoteObject extends RemoteObject {
	
	public var mock:Mock;
	
	public function MockRemoteObject(destination:String = null) {
		super(destination);
		this.mock = new Mock(this);
	}
	
	override public function getOperation(name:String):AbstractOperation {
		return this.mock.getOperation(name);
	}
	
}
*/

/**
 * Mock operation
 * @private
 */
/*
class MockOperation extends Operation {
	
	public var mock:Mock;
	
	public function MockOperation(remoteObject:AbstractService = null, name:String = null) {
		super(remoteObject,name);
		this.mock = new Mock(this);
	}
	
	override public function send(... args):AsyncToken {
		return this.mock.send(args);
	}
	
}
*/


/**
 * Object bindable just for test
 * @private
 */
/*
[Bindable]
class ItemBindable extends Object {
	public var propWillChange:int;
}
*/